#pragma once
/*
    对Dispatcher消息分发模块的实现
    -1. 维护映射关系
    -2. 注册映射关系的接口
    -3. 提供给网络的业务处理接口

    优化:
      使用模板以及C++的多态特性,从而实现在用户层可以直接使用具体的消息类型,相应的可调用接口变得更多
      [之前是BaseMessage类型,可调用的方法没有具体的派生类那么丰富]
*/
#include "net.hpp"
#include "message.hpp"

namespace tmh_rpc
{
    class Callback
    {
    public:
        using ptr = std::shared_ptr<Callback>;
        virtual void onMessage(const BaseConnection::ptr &conn, BaseMessage::ptr &msg)=0;

    };

    template <typename T>
    class CallbackT : public Callback
    {
    public:
        using ptr = std::shared_ptr<CallbackT<T>>;

        using MessageCallback=std::function<void (const BaseConnection::ptr &conn, std::shared_ptr<T> &msg)>;
        CallbackT(const MessageCallback &handler) : _handler(handler)
        {
        }
        void onMessage(const BaseConnection::ptr &conn, BaseMessage::ptr &msg) override
        {
            // 将BaseMessage的数据改成特定类型的数据
            auto type_msg = std::dynamic_pointer_cast<T>(msg);
            _handler(conn, type_msg);
        }

    private:
        MessageCallback _handler;
    };
    class Dispatcher
    {
    public:
        using ptr = std::shared_ptr<Dispatcher>;

        // 注册消息类型和回调之间的映射关系
        // v2. 设置模板,使得用户可以根据不同的回调函数进行方法调用
        template <typename T>
        void registerHandler(MType mtype, const typename CallbackT<T>::MessageCallback &handler)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto cb = std::make_shared<CallbackT<T>>(handler);
            _handlers.insert({mtype, cb});
        }

        // 找到消息(msg)类型(mtype)对应的业务处理函数,调用即可
        void onMessage(const BaseConnection::ptr &conn, BaseMessage::ptr &msg)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _handlers.find(msg->mtype());
            if (it != _handlers.end())
            {
                // second就是一个对应消息的回调处理函数
                it->second->onMessage(conn, msg);
                return; // 处理完了请求之后不应该向下关闭连接
            }
            // 没找到类型的处理函数--恶意客户端
            ELOG("收到未知类型的消息!");
            conn->shutdown(); // 关闭连接
        }

    private:
        std::mutex _mutex;

        // 构建映射关系
        // 使用多态,让父类指针指向子类对象从而实现容器存储不同类型的对象
        std::unordered_map<MType, Callback::ptr> _handlers;
    };
}